#pragma once
#include <iostream>
#include "protocol.hpp"
#include "connection.hpp"
#include "Calculate.hpp"
class HandlerConnection
{
    // 从字节流数据中获取请求，并处理
    static void HandlerRequest(Connection *conn)
    {
        // 缓冲区的字节流
        std::string &package = conn->GetInBuffer();
        // 一个完整的报文
        std::string message;
        // 计算器业务类
        Calculate::Calculate calculate;
        // 创建请求和回应的工厂
        Protocol::Factory factory;
        auto request = factory.BuildRequest();
        auto response = factory.BuildResponse();

        // 1.进行报文分割
        while (Protocol::Decode(package, &message) == true)
        {
            // message已经是一个完整的报文！！！

            // 2.进行反序列化
            if (request->Deserialize(message) == false)
            {
                continue; // 反序列化失败，但是获取到了一个完整报文
            }
            // 3.处理业务
            calculate.Cal(request);
            // 4.对响应进行序列化
            std::string response_str;
            if (response->Serialize(&response_str) == false)
            {
                continue;
            }
            // 5.封装完整报文
            response_str += Protocol::Encode(response_str);

            // 6.将应答，追加到outbuffer
            conn->AddOutbuffer(response_str);
        }

        // 进行数据报文的发送
        if (conn->OutBufferEmpty() == true)
        {
            conn->_send_func(conn); // 直接进行发送，可能没有全部发送
        }
    }

public:
    // 提供注册的处理函数
    static void Recver(Connection *conn)
    {
        errno = 0;
        lg.LogMessage(Info, "recver IO call success\n");
        char buffer[1024];
        while (1)
        {
            // 接收字节流数据
            ssize_t n = recv(conn->GetSockFd(), buffer, sizeof(buffer) - 1, 0);
            if (n > 0)
            {
                buffer[n] = 0;
                // 将数据写入conn的输入缓冲区
                conn->AddInbuffer(buffer);
            }
            else
            {
                // 本轮读完
                if (errno == EAGAIN)
                {
                    break;
                }
                // 读取时被信号中断，需要重新读取
                else if (errno == EINTR)
                {
                    continue;
                }
                // 读取错误
                else
                {
                    conn->_except_func(conn);
                    return;
                }
            }
        }
        // 打印inbuffer
        std::cout << "sock fd: " << conn->GetSockFd() << std::endl;
        std::cout << "Inbuffer: " << conn->GetInBuffer() << std::endl;
        // 分析报文，报文是否完整，引入协议, 处理报文
        HandlerRequest(conn);
    }
    static void Sender(Connection *conn)
    {
        errno = 0;
        // 写处理：
        // 1.直接发，此时发送缓冲区一定是有空间的 2.如果发送缓冲区满了，但是数据仍存在，这时就需要关心写事件

        lg.LogMessage(Info, "sender IO success\n");
        std::string &outbuffer = conn->GetOutBuffer(); // 用引用直接寻址找到底层，这样可以去掉已发送的数据
        while (1)
        {
            ssize_t n = send(conn->GetSockFd(), outbuffer.c_str(), outbuffer.size(), 0);
            if (n > 0)
            {
                // 通过引用直接把对应Connection对应的fd的对应的缓冲区，去掉已发送的数据，也可以在Connection内部实现
                outbuffer.erase(0, n);
                if (outbuffer.empty() == true)
                {
                    break;
                }
            }
            else
            {
                if (errno == EAGAIN)
                {
                    break; // 数据没发完但是出问题了？可能是发送缓冲区满了
                }
                else if (errno == EINTR)
                {
                    continue;
                }
                else
                {
                    // 出异常
                    conn->_except_func(conn);
                    return;
                }
            }
        }

        // 本轮发完了，但是发送缓冲区满了？这时开始关心这个文件描述符的发送事件，这里也就是epoll写的核心
        if (conn->OutBufferEmpty() == false)
        {
            conn->_R->EnableIO(conn->GetSockFd(), true, true);
        }
        else
        {
            conn->_R->EnableIO(conn->GetSockFd(), true, false); // 如果没满，那么就可以不需要关心了！！！
        }
    }
    static void Excepter(Connection *conn)
    {
        errno = 0;
        lg.LogMessage(Info, "excepter IO success\n");

        // IO出现异常，处理链接和相关事件
        int sock_fd = conn->GetSockFd();
        conn->_R->CloseConnection(conn->GetSockFd());
        lg.LogMessage(Info, "connection erase success, fd = %d\n", sock_fd);

        // 释放这个指针
        delete conn;
    }
};